package com.zyy.game.tianlong.network;

import com.zyy.game.tianlong.network.config.NettyConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;

/**
 * netty服务
 *
 * @author relufi
 * @date 2019/7/22 0022 08:59
 */
@Component
@Slf4j
public class NettyServer {
    private final NettyConfig nettyConfig;
    private final EventExecutorGroup businessGroup;
    private final ChannelInitializer channelInitializer;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workGroup;
    private ServerBootstrap bootstrap = new ServerBootstrap();

    public NettyServer(NettyConfig nettyConfig, EventExecutorGroup businessGroup, ChannelInitializer channelInitializer) {
        this.nettyConfig = nettyConfig;
        this.businessGroup = businessGroup;
        this.channelInitializer = channelInitializer;
    }


    /**
     * 启动Netty Server
     */
    @PostConstruct
    public void start() {
        //boss 线程
        this.bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("boss线程"));
        //work 线程
        this.workGroup = new NioEventLoopGroup(nettyConfig.getWorkGroup(), new DefaultThreadFactory("work线程"));
        bootstrap.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(nettyConfig.getPort()))
                //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                .option(ChannelOption.SO_BACKLOG, nettyConfig.getBacklog())
                //设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, nettyConfig.isKeepalive())
                .childHandler(channelInitializer);
        log.info("端口号: " + nettyConfig.getPort());
        ChannelFuture future = bootstrap.bind().syncUninterruptibly();

        if (future.isSuccess()) {
            log.info("Netty 服务 启动 ");
        }
    }

    /**
     * 释放线程池资源
     */
    @PreDestroy
    public void destroy() {
        bossGroup.shutdownGracefully().syncUninterruptibly();
        workGroup.shutdownGracefully().syncUninterruptibly();
        businessGroup.shutdownGracefully().syncUninterruptibly();
        log.info("Netty 服务 结束 ");
    }
}
